package com.coszero.common.network.ftp;

import android.util.Log;


import com.coszero.utils.utils.LogX;
import com.coszero.utils.utils.StringUtils;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;

/**
 * Desc： FTP上传工具类
 * 需要借助 commons-net.jar包
 *
 * @author xmqian
 * Email:xmqian93@163.com
 * Date: 2021/1/5 0005 09:30
 * @link(https://blog.csdn.net/qq_30993595/article/details/80451523)
 */
public class FTPHelper implements FTPInterface {
    public static final String TAG = "ftpUtils";

    /**
     * 服务器地址
     */
    private final String ftpHost = FTPConfig.FTP_HOST;

    /**
     * 端口
     */
    private final int ftpPort = FTPConfig.FTP_PORT;

    /**
     * 用户名
     */
    private final String ftpUserName = "njzdr";

    /**
     * 密码
     */
    private final String ftpPassword = "123456";
    private static FTPClient ftpClient;

    /**
     * 当前所在的ftp文件夹路径
     */
    private String currentFtpDirPath = "";

    public static FTPHelper newInstance() {
        FTPHelper ftpHelper = new FTPHelper();
        return ftpHelper;
    }

    private FTPHelper() {
        new IllegalAccessException();
    }

    /**
     * 本地文件是否存在,并且大小大于0
     *
     * @param file 待上传文件
     * @return true:存在可上传
     */
    private boolean localFileIsExists(File file) {
        return file != null && file.exists() && file.isFile() && file.length() > 0;
    }

    public boolean upload(String path, File file) {
        try {
            if (localFileIsExists(file)) {
                //判断文件存储,并且是文件,并且大小大于0才进行上传
                FileInputStream fileInputStream = new FileInputStream(file);
                upload(ftpHost, 0, ftpUserName, ftpPassword, path, file.getName(), fileInputStream);
                return true;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 向FTP服务器上传文件
     *
     * @param url FTP服务器hostname 就是ip
     * @param port 21
     * 端口默认80 ,0会使用默认端口
     * @param username 用户名
     * @param password 密码
     * @param path FTP服务器保存目录 示例: test/video 为空就不创建
     * @param filename 文件名称 上传到FTP服务器上的文件名,是自己定义的名字
     * @param input 文件输入流
     * @return
     */
    public boolean upload(String url, int port, String username,
                          String password, String path, String filename, InputStream input) {

        boolean success = false;
        ftpClient = new FTPClient();

        try {
            ftpClient.setDataTimeout(2000);//设置连接超时时间
            if (port == 0) {
                ftpClient.connect(url);
            } else {
                ftpClient.connect(url, port);
            }
            // 如果采用默认端口，可以使用ftp.connect(url)的方式直接连接FTP服务器
            ftpClient.login(username, password);
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                ftpClient.disconnect();  //未连接到FTP，用户名或密码错误
                LogX.w(TAG, "未连接到FTP，用户名或密码错误");
                return false;
            }

            if (!StringUtils.isEmpty(path)) {
                boolean isExist = createDirecroty(path, ftpClient);
                if (!isExist) {
                    return false;
                }
            }

            //获取服务器当前目录指定文件信息
            FTPFile[] files = ftpClient.listFiles(filename);
            if (files.length > 1) {
                ftpClient.deleteFile(filename);
            }

            ftpClient.setControlEncoding("UTF-8");
            ftpClient.setBufferSize(1024);
            ftpClient.enterLocalPassiveMode();
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            //处理中文名称的文件名，如果不加这一句的话，中文命名的文件是不能上传的
            filename = new String(filename.getBytes("GBK"), StandardCharsets.ISO_8859_1);
            ftpClient.storeFile(filename, input);

            input.close();
            ftpClient.logout();
            success = true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
        }
        return success;
    }

    /**
     * 递归创建远程服务器目录
     *
     * @param remote 远程服务器文件绝对路径
     * @return 目录创建是否成功
     * @throws IOException
     */
    public boolean createDirecroty(String remote, FTPClient ftp) throws IOException {

        String totalPath = "";
        boolean success = true;
        String[] path = remote.split("/");
        LogX.w(TAG, "需要创建的目录层级为：" + path.length);
        for (int i = 0; i < path.length; i++) {
            String father = path[i];
            if (father == null || "".equals(father)) {
                success = false;
                break;
            }
            totalPath = totalPath + "/" + father;
            try {
                boolean isExit = ftp.changeWorkingDirectory(totalPath);
                if (!isExit) {
                    boolean make = ftp.makeDirectory(totalPath);
                    if (!make) {
                        success = false;
                        break;
                    }
                    //工作路径切换到此
                    boolean change = ftp.changeWorkingDirectory(totalPath);
                    if (!change) {
                        success = false;
                        break;
                    }
                }
            } catch (IOException e) {
                success = false;
                break;
            }
        }
        return success;

    }

    public boolean upload(String url, int port, String username,
                          String password, String remotePath, File localFile) {

        boolean success = false;
        RandomAccessFile raf = null;
        OutputStream output = null;
        FTPClient ftp = new FTPClient();

        try {
            ftp.connect(url, port);
            // 如果采用默认端口，可以使用ftp.connect(url)的方式直接连接FTP服务器  
            ftp.login(username, password);
            int reply = ftp.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                LogX.w(TAG, "未连接到FTP，用户名或密码错误");
                ftp.disconnect();  //未连接到FTP，用户名或密码错误
                return success;
            }

            boolean isExist = createDirecroty(remotePath, ftp);
            if (!isExist) {
                return success;
            }

            //获取当前目录指定文件信息
            FTPFile[] files = ftp.listFiles(localFile.getName());
            if (files.length > 1) {
                ftp.deleteFile(localFile.getName());
            }

            raf = new RandomAccessFile(localFile, "r");
            long serverSize = 0;
            /* enterLocalPassiveMode
             * 每次数据连接之前，ftp client告诉ftp server开通一个端口来传输数据。
             * 为什么要这样做呢，因为ftp server可能每次开启不同的端口来传输数据，
             * 但是在linux上或者其他服务器上面，由于安全限制，可能某些端口没有开启，
             * 所以就出现阻塞
             * */
            ftp.enterLocalPassiveMode();
            ftp.setFileType(FTP.BINARY_FILE_TYPE);//设置二进制传输，还支持传输ACSII数据  
            ftp.setRestartOffset(serverSize);
            raf.seek(serverSize);

            // 进度  
            long localSize = localFile.length(); // 本地文件的长度 
            long step = localSize / 100;
            long process = 0;
            long currentSize = 0;
            String filename = localFile.getName();
            filename = new String(filename.getBytes("GBK"), StandardCharsets.ISO_8859_1);
            output = ftp.appendFileStream(filename);
            byte[] b = new byte[1024];
            int length = 0;
            while ((length = raf.read(b)) != -1) {
                output.write(b, 0, length);
                currentSize = currentSize + length;
                if (currentSize / step != process) {
                    process = currentSize / step;
                    Log.e(TAG, "上传进度：" + process);
                }
            }

            if (ftp.completePendingCommand()) {
                success = true;
                Log.e(TAG, "文件上传成功");
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            try {
                output.flush();
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                raf.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }

            try {
                ftp.logout();
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException ioe) {
                }
            }
        }
        return success;

    }


    public static boolean downloadFile(String url, int port, String username,
                                       String password, String localPath, String serverPath) {

        boolean success = false;
        FTPClient ftpClient = new FTPClient();

        try {
            ftpClient.setControlEncoding("GBK");
            ftpClient.connect(url, port);
            ftpClient.login(username, password);
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                ftpClient.disconnect();  //未连接到FTP，用户名或密码错误
                return false;
            }

            // 先判断服务器文件是否存在
            FTPFile[] files = ftpClient.listFiles(serverPath);
            if (files.length == 0) {
                Log.e(TAG, "服务器文件不存在 serverPath=" + serverPath);
                return false;
            }

            localPath = localPath + files[0].getName();
            long serverSize = files[0].getSize(); // 获取远程文件的长度
            File localFile = new File(localPath);
            long localSize = 0;
            if (localFile.exists()) {
                localFile.delete();
            }
            // 进度
            long step = serverSize / 100;
            long process = 0;
            long currentSize = 0;
            // 开始准备下载文件
            ftpClient.enterLocalActiveMode();
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            OutputStream out = new FileOutputStream(localFile, true);
            ftpClient.setRestartOffset(localSize); //设置从哪里开始下，就是断点下载
            InputStream input = ftpClient.retrieveFileStream(serverPath);
            byte[] b = new byte[1024];
            int length = 0;
            while ((length = input.read(b)) != -1) {
                out.write(b, 0, length);
                currentSize = currentSize + length;
                if (currentSize / step != process) {
                    process = currentSize / step;
                    Log.e(TAG, "下载进度：" + process);
                }
            }
            out.flush();
            out.close();
            input.close();
            // 此方法是来确保流处理完毕，如果没有此方法，可能会造成现程序死掉
            if (ftpClient.completePendingCommand()) {
                Log.e(TAG, "文件下载成功");
                success = true;
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return success;

    }

    @Override
    public boolean ftpConnect(String host, String username, String password, int port) {
        try {
            ftpClient = new FTPClient();
            //设置连接超时时间
            ftpClient.setDataTimeout(2000);
            Log.d(TAG, "connecting to the ftp server " + host + " ：" + port);

            if (port == 0) {
                ftpClient.connect(host);
            } else {
                ftpClient.connect(host, port);
            }

            // 根据返回的状态码，判断链接是否建立成功
            if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                Log.d(TAG, "login to the ftp server");
                boolean status = ftpClient.login(username, password);
                /*
                 * 设置文件传输模式
                 * 避免一些可能会出现的问题，在这里必须要设定文件的传输格式。
                 * 在这里我们使用BINARY_FILE_TYPE来传输文本、图像和压缩文件。
                 */
                ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
                ftpClient.enterLocalPassiveMode();
                LogX.i(TAG, "登录FTP状态 false:登录密码错误: status=" + status);
                return status;
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG, "Error: could not connect to host " + host);
        }
        return false;
    }

    @Override
    public boolean ftpDisconnect() {
        // 判断空指针
        if (ftpClient == null) {
            return true;
        }

        // 断开ftp服务器连接
        try {
            ftpClient.logout();
            if (ftpClient.isConnected()) {
                ftpClient.disconnect();
            }
            return true;
        } catch (Exception e) {
            Log.d(TAG, "Error occurred while disconnecting from ftp server.");
        }
        return false;
    }

    @Override
    public boolean ftpUpload(String srcFilePath, String desFileName, String desDirectory) {
        boolean status = false;
        try {
            FileInputStream srcFileStream = new FileInputStream(srcFilePath);

            ftpClient.setControlEncoding("UTF-8");
            ftpClient.setBufferSize(1024);
            ftpClient.enterLocalPassiveMode();
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

            //处理中文名称的文件名，如果不加这一句的话，中文命名的文件是不能上传的
            desFileName = new String(desFileName.getBytes("GBK"), StandardCharsets.ISO_8859_1);

            status = ftpClient.storeFile(desFileName, srcFileStream);
            srcFileStream.close();
            return status;
        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG, "upload failed: " + e.getLocalizedMessage());
        }
        return status;
    }

    @Override
    public void ftpUploadProgress(String srcFilePath, String desFileName, String desDirectory, FTPUploadListener listener) {

    }

    @Override
    public boolean ftpChangeDir(String path) {
        boolean status = false;
        try {
            status = ftpClient.changeWorkingDirectory(path);
        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG, "change directory failed: " + e.getLocalizedMessage());
        }
        return status;
    }

    @Override
    public boolean ftpMakeDir(String dirPath) {
        boolean status = false;
        try {
            status = ftpClient.makeDirectory(dirPath);
        } catch (IOException e) {
            e.printStackTrace();
            Log.d(TAG, "make directory failed: " + e.getLocalizedMessage());
        }
        return status;
    }

    @Override
    public String ftpWorkingDir() {
        try {
            String workingDirectory = ftpClient.printWorkingDirectory();
            currentFtpDirPath = workingDirectory;
            LogX.i(TAG, "当前ftp远程文件夹路径:" + workingDirectory);
            return workingDirectory;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    @Override
    public boolean ftpFileIsExists(String fileName, boolean deleteFile) {
        //获取服务器当前目录指定文件信息
        FTPFile[] files;
        try {
            files = ftpClient.listFiles(fileName);
            if (files.length > 1) {
                if (deleteFile) {
                    //将文件删除就相当于不存在
                    ftpClient.deleteFile(fileName);
                } else {
                    return true;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public void uploadSingleFile(String singleFilePath, String remotePath, String remoteFileName,FTPUploadListener listener) {
        //连接服务
        boolean ftpConnect = ftpConnect(ftpHost, ftpUserName, ftpPassword, ftpPort);
        if (ftpConnect) {
            //切换工作目录
            boolean ftpChangeDir = ftpChangeDir(remotePath);
            if (ftpChangeDir) {
                boolean ftpUpload = ftpUpload(singleFilePath, remoteFileName, remotePath);
                if (ftpUpload) {
                    listener.success();
                    boolean ftpDisconnect = ftpDisconnect();
                    LogX.i(TAG, "FTP断开状态:" + ftpDisconnect);
                }
            } else {
                listener.faile();
                LogX.w(TAG, "ftp改变工作空间失败");
            }
        } else {
            listener.faile();
            LogX.w(TAG, "ftp连接失败");
        }

    }

    @Override
    public void uploadMultiFile(LinkedList<File> fileList, String remotePath, FTPUploadListener listener) {

    }
}
